#include "imgjointtry.h"


ImgJointTry::ImgJointTry(QObject *parent) : QObject(parent)
{

}

void ImgJointTry::merg2Image(QString dstImageDir,QString leftImgPath,QString rightImgPath)
{
    m_resultImageDir = dstImageDir;

    std::string leftImagePath = leftImgPath.toLocal8Bit().toStdString();
    std::string rightImagePath = rightImgPath.toLocal8Bit().toStdString();

    image_left = imread(leftImagePath); // , IMREAD_GRAYSCALE
    image_right = imread(rightImagePath);// , IMREAD_GRAYSCALE

    //判断图像是否加载成功
    if (image_left.empty() || image_right.empty())
    {
        cout << "图像加载失败";
        return ;
    }
    else
        cout << "Images loaded success..." << endl;
    //SIFT：特征检测
    Ptr <SIFT> detector = SIFT::create();
    //vector <KeyPoint> kp1, kp2;
    //KeyPoint：图像特征的关键点
    Mat descriptorLeft, descriptorRight;
//    image：输入的图像，作为目标检测和特征提取的输入
//    keypoints：检测到的关键点的位置信息，以向量形式表示
//    descriptors：与每个关键点关联的特征描述子，以矩阵形式表示
    detector->detectAndCompute(image_right, noArray(), mKeyRight, descriptorRight);
    detector->detectAndCompute(image_left, noArray(), mKeyLeft, descriptorLeft);

    //根据关键点信息输出图像
    Mat outimageLeft;
    Mat outimageRight;
    drawKeypoints(image_left, mKeyLeft, outimageLeft);
    drawKeypoints(image_right, mKeyRight, outimageRight);

    string szDstDir = m_resultImageDir.toLocal8Bit().toStdString();
    string szLeftKptPicPath = szDstDir + "\\keyPointsLeft.jpg";
    imwrite(szLeftKptPicPath, outimageLeft);
    string szRightKptPicPath = szDstDir + "\\keyPointsRight.jpg";
    imwrite(szRightKptPicPath, outimageRight);

    vector<DMatch> matches;    //DMatch是用来描述匹配好的一对特征点的类，包含这两个点之间的相关信息
                               //比如左图有个特征m，它和右图的特征点n最匹配，这个DMatch就记录它俩最匹配，并且还记录m和n的
                               //特征向量的距离和其他信息，这个距离在后面用来做筛选
    BFMatcher matcher;         //实例化一个暴力匹配器
    matcher.match(descriptorLeft, descriptorRight, matches);             //匹配，数据来源是特征向量，结果存放在DMatch类型里面

    //sort函数对数据进行升序排列
    sort(matches.begin(), matches.end());     //筛选匹配点，根据match里面特征对的距离从小到大排序
    //vector< DMatch > good_matches;
    int ptsPairs = std::min(50, int(matches.size() * 0.15));
    cout << ptsPairs << endl;
    for (int i = 0; i < ptsPairs; i++)
    {
        mGood_matches.push_back(matches[i]);//距离最小的50个压入新的DMatch
    }
    cout << "merg2Image over!" << endl;
}

void ImgJointTry::imageMelt()
{
    Mat outimg;

    //drawMatches这个函数直接画出摆在一起的图
    drawMatches(image_left, mKeyLeft, image_right, mKeyRight, mGood_matches, outimg,
                Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);  //绘制匹配点

    string szDstDir = m_resultImageDir.toLocal8Bit().toStdString();
    string szMatchPicPath = szDstDir + "\\matchPic.jpg";
    imwrite(szMatchPicPath, outimg);

    ///////////////////////图像配准及融合////////////////////////

    vector<Point2f> imagePointsRight, imagePointsLeft;

    for (size_t i = 0; i<mGood_matches.size(); i++)
    {
        imagePointsLeft.push_back(mKeyLeft[mGood_matches[i].queryIdx].pt);
        imagePointsRight.push_back(mKeyRight[mGood_matches[i].trainIdx].pt);
    }

    //获取图像1到图像2的投影映射矩阵 尺寸为3*3
    Mat homo = findHomography(imagePointsRight, imagePointsLeft, CV_RANSAC);
    ////也可以使用getPerspectiveTransform方法获得透视变换矩阵，不过要求只能有4个点，效果稍差
    //Mat   homo=getPerspectiveTransform(imagePoints1,imagePoints2);
    cout << "变换矩阵为：\n" << homo << endl << endl; //输出映射矩阵

    //计算配准图的四个顶点坐标
    CalcCorners(homo, image_right); // 输出到全局变量corners
    cout << "left_top:" << corners.left_top << endl;
    cout << "left_bottom:" << corners.left_bottom << endl;
    cout << "right_top:" << corners.right_top << endl;
    cout << "right_bottom:" << corners.right_bottom << endl;

    //图像配准
    Mat imageTransformRight;
    warpPerspective(image_right, imageTransformRight, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), image_left.rows));
    //warpPerspective(a, imageTransform2, adjustMat*homo, Size(b.cols*1.3, b.rows*1.8));
    //imshow("直接经过透视矩阵变换", imageTransform1);
    //imwrite("trans1.jpg", imageTransform1);
    string szTransform1Path = szDstDir + "\\Transform1.jpg";
    imwrite(szTransform1Path, imageTransformRight);

    //创建拼接后的图,需提前计算图的大小
    int dst_width = imageTransformRight.cols;  //取最右点的长度为拼接图的长度
    int dst_height = image_left.rows;

    Mat dst(dst_height, dst_width, CV_8UC3);
    dst.setTo(0);

    imageTransformRight.copyTo(dst(Rect(0, 0, imageTransformRight.cols, imageTransformRight.rows)));
    image_left.copyTo(dst(Rect(0, 0, image_left.cols, image_left.rows)));

    //imshow("b_dst", dst);
    string szMerge1Path = szDstDir + "\\Merge1.jpg";
    imwrite(szMerge1Path, dst);

    OptimizeSeam(image_left, imageTransformRight, dst);

    //imshow("dst", dst);
    //imwrite("dst.jpg", dst);
    string szMergeLastPath = szDstDir + "\\MergeLast.jpg";
    imwrite(szMergeLastPath, dst);
}


void ImgJointTry::CalcCorners(const cv::Mat& H, const cv::Mat& src)
{
    double v2[] = { 0, 0, 1 };//左上角
    double v1[3];//变换后的坐标值
    Mat V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    Mat V1 = Mat(3, 1, CV_64FC1, v1);  //列向量

    V1 = H * V2;
    //左上角(0,0,1)
    cout << "V2: " << V2 << endl;
    cout << "V1: " << V1 << endl;
    corners.left_top.x = v1[0] / v1[2];
    corners.left_top.y = v1[1] / v1[2];

    //左下角(0,src.rows,1)
    v2[0] = 0;
    v2[1] = src.rows;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.left_bottom.x = v1[0] / v1[2];
    corners.left_bottom.y = v1[1] / v1[2];

    //右上角(src.cols,0,1)
    v2[0] = src.cols;
    v2[1] = 0;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.right_top.x = v1[0] / v1[2];
    corners.right_top.y = v1[1] / v1[2];

    //右下角(src.cols,src.rows,1)
    v2[0] = src.cols;
    v2[1] = src.rows;
    v2[2] = 1;
    V2 = Mat(3, 1, CV_64FC1, v2);  //列向量
    V1 = Mat(3, 1, CV_64FC1, v1);  //列向量
    V1 = H * V2;
    corners.right_bottom.x = v1[0] / v1[2];
    corners.right_bottom.y = v1[1] / v1[2];
}

//优化两图的连接处，使得拼接自然
void ImgJointTry::OptimizeSeam(cv::Mat& img1, cv::Mat& trans, cv::Mat& dst)
{
    int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置，即重叠区域的左边界

    double processWidth = img1.cols - start;//重叠区域的宽度
    int rows = dst.rows;
    int cols = img1.cols; //注意，是列数*通道数
    double alpha = 1;//img1中像素的权重
    for (int i = 0; i < rows; i++)
    {
        uchar* p = img1.ptr<uchar>(i);  //获取第i行的首地址
        uchar* t = trans.ptr<uchar>(i);
        uchar* d = dst.ptr<uchar>(i);
        for (int j = start; j < cols; j++)
        {
            //如果遇到图像trans中无像素的黑点，则完全拷贝img1中的数据
            if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)
            {
                alpha = 1;
            }
            else
            {
                //img1中像素的权重，与当前处理点距重叠区域左边界的距离成正比，实验证明，这种方法确实好
                alpha = (processWidth - (j - start)) / processWidth;
            }

            d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);
            d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);
            d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);

        }
    }

}
